package cn.xydpeng.springframework.beans.factory.support;

import cn.xydpeng.springframework.beans.BeanUtils;
import cn.xydpeng.springframework.beans.BeanWrapper;
import cn.xydpeng.springframework.beans.BeanWrapperImpl;
import cn.xydpeng.springframework.beans.BeansException;
import cn.xydpeng.springframework.beans.PropertyValue;
import cn.xydpeng.springframework.beans.PropertyValues;
import cn.xydpeng.springframework.beans.factory.Aware;
import cn.xydpeng.springframework.beans.factory.BeanClassLoaderAware;
import cn.xydpeng.springframework.beans.factory.BeanFactoryAware;
import cn.xydpeng.springframework.beans.factory.BeanNameAware;
import cn.xydpeng.springframework.beans.factory.InitializingBean;
import cn.xydpeng.springframework.beans.factory.config.AutowireCapableBeanFactory;
import cn.xydpeng.springframework.beans.factory.config.BeanPostProcessor;
import cn.xydpeng.springframework.beans.factory.config.BeanReference;
import cn.xydpeng.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import cn.xydpeng.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import cn.xydpeng.springframework.lang.Nullable;
import cn.xydpeng.springframework.util.ObjectUtils;
import cn.xydpeng.springframework.util.ReflectionUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * @author xydpeng
 * @version v1.0
 * @date: 2022/10/12
 * @description:
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {

    String SCOPE_PROTOTYPE = "prototype";

    /** Whether to automatically try to resolve circular references between beans. */
    private boolean allowCircularReferences = true;

    /**
     * Strategy for creating bean instances.
     */
    private InstantiationStrategy instantiationStrategy;

    public InstantiationStrategy getInstantiationStrategy() {
        return instantiationStrategy;
    }

    public AbstractAutowireCapableBeanFactory() {
        super();
        this.instantiationStrategy = new CglibSubclassingInstantiationStrategy();
    }


    @Override
    public <T> T createBean(Class<T> beanClass) throws BeansException {
        RootBeanDefinition mbd = new RootBeanDefinition(beanClass);
        mbd.setScope(SCOPE_PROTOTYPE);
        return (T) createBean(beanClass.getName(), mbd, null);
    }

    @Override
    protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeansException {

        RootBeanDefinition mbdToUse = mbd;

        //TODO 一个扩展点
        //Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
        // ---bean实例化扩展点InstantiationAwareBeanPostProcessor,在doCreateBean之前执行InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
            return bean;
        }
        //开始创建bean
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);

        return beanInstance;
    }



    protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
        //TODO
        Object bean = null;
        if(hasInstantiationAwareBeanPostProcessors()){
            Class<?> targetType = mbd.getBeanClass();
            //触发bean实例化之前的钩子 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(),
            //生成bean不为null,则代替执行doCreate()方法,直接返回
            bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
            if (bean != null) {
                bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); //初始化后置回调
            }
        }
        return bean;
    }


    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeansException {

        // Instantiate the bean.
        BeanWrapper instanceWrapper = null;
        //1.创建bean对象，并包装成BeanWrapper
        instanceWrapper = createBeanInstance(beanName, mbd, args);
        Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();

        // Eagerly cache singletons to be able to resolve circular references
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            //2. 生成objectFactory并放入第三级缓存
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

        // Initialize the bean instance. --- 实例化bean
        Object exposedObject = bean;
        try {
            //3. 注入bean依赖
            populateBean(beanName, mbd, instanceWrapper);
            //4. 触发 Bean实现的Aware接口钩子、 init初始化方法和 BeanPostProcessor 的前置和后置处理方法
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        } catch (Exception ex) {
            throw new RuntimeException("populateBean/initializeBean error,bean: " + beanName, ex);
        }

        // Register bean as disposable. --- 注册实现了 DisposableBean 接口的 Bean 对象
        registerDisposableBeanIfNecessary(beanName, bean, mbd);

        return exposedObject;
    }

    /**
     * Obtain a reference for early access to the specified bean,
     * typically for the purpose of resolving a circular reference. --- 三级缓存的ObjectFactory核心逻辑，触发SmartInstantiationAwareBeanPostProcessor。getEarlyBeanReference执行
     * @param beanName the name of the bean (for error handling purposes)
     * @param mbd the merged bean definition for the bean
     * @param bean the raw bean instance
     * @return the object to expose as bean reference
     */
    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
                exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
        return exposedObject;
    }

    @Nullable
    protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
        for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
            Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
            if (result != null) {
                return result;
            }
        }
        return null;
    }


    /**
     * Create a new instance for the specified bean, using an appropriate instantiation strategy:
     * factory method, constructor autowiring, or simple instantiation.
     */
    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
        //1、 TODO Make sure bean class is actually resolved at this point.
        Class<?> beanClass =mbd.getBeanClass(); //resolveBeanClass(mbd, beanName);


        //2、扩展点钩子SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors()，确定使用那个构造函数
        // Candidate constructors for autowiring?
        Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
        if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
                mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
            return autowireConstructor(beanName, mbd, ctors, args);
        }


        //3、 No special handling: simply use no-arg constructor. --- 使用无参构造函数
        return instantiateBean(beanName, mbd);
    }



    /**
     * Determine candidate constructors to use for the given bean, checking all registered
     * {@link SmartInstantiationAwareBeanPostProcessor SmartInstantiationAwareBeanPostProcessors}.
     * @param beanClass the raw class of the bean
     * @param beanName the name of the bean
     * @return the candidate constructors, or {@code null} if none specified
     * @throws org.springframework.beans.BeansException in case of errors
     * @see SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors
     */
    @Nullable
    protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName)
            throws BeansException {

        if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
            for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
                Constructor<?>[] ctors = bp.determineCandidateConstructors(beanClass, beanName);
                if (ctors != null) {
                    return ctors;
                }
            }
        }
        return null;
    }


    /**
     * "autowire constructor" (with constructor arguments by type) behavior.
     * Also applied if explicit constructor argument values are specified,
     * matching all remaining arguments with beans from the bean factory.
     * <p>This corresponds to constructor injection: In this mode, a Spring
     * bean factory is able to host components that expect constructor-based
     * dependency resolution.
     * @param beanName the name of the bean
     * @param mbd the bean definition for the bean
     * @param ctors the chosen candidate constructors
     * @param explicitArgs argument values passed in programmatically via the getBean method,
     * or {@code null} if none (implying the use of constructor argument values from bean definition)
     * @return a BeanWrapper for the new instance
     */
    protected BeanWrapper autowireConstructor(
            String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {
        //TODO 暂未实现
        return null;
    }

    /**
     * Instantiate the given bean using its default constructor.
     * @param beanName the name of the bean
     * @param mbd the bean definition for the bean
     * @return a BeanWrapper for the new instance
     */
    protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {
        try {
            Object beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);
            BeanWrapper bw = new BeanWrapperImpl(beanInstance);
            //initBeanWrapper(bw);
            return bw;
        } catch (Throwable ex) {
            throw new RuntimeException(beanName+" BeanCreationException:", ex);
        }
    }

    /**
     * Populate the bean instance in the given BeanWrapper with the property values
     * from the bean definition.  --- 填充bean实例
     *
     * @param beanName the name of the bean
     * @param mbd      the bean definition for the bean
     * @param bw       the BeanWrapper with bean instance
     */
    protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {

        //执行扩展点钩子：InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()，返回boolean类型，false则短路populateBean执行
        // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
        // state of the bean before properties are set. This can be used, for example,
        // to support styles of field injection.
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
                if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                    return;
                }
            }
        }

        //注入bean依赖
        PropertyValues pvs = mbd.getPropertyValues();

        //执行扩展点钩子：InstantiationAwareBeanPostProcessor.postProcessProperties()，可在注入bean的属性之前，修改属性值
        if (hasInstantiationAwareBeanPostProcessors()) {
            if (pvs == null) {
                pvs = mbd.getPropertyValues();
            }
            for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
                PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
                if (pvsToUse == null) {
                    return;
                }
                pvs = pvsToUse;
            }
        }

        //开始处理bean的属性注入
        applyPropertyValues(beanName, mbd, bw, pvs);
    }


    protected void applyPropertyValues(String beanName, RootBeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
        PropertyValues propertyValues = pvs;
        for (PropertyValue pv : propertyValues.getPropertyValues()) {
            String name = pv.getName();
            Object orgValue = pv.getValue();
            Object property = null;
            if (orgValue instanceof BeanReference) {
                BeanReference beanReference = (BeanReference) orgValue;
                property = getBean(beanReference.getBeanName());
            } else {
                //TODO 类型转化
                property = orgValue;
            }
            BeanUtils.setFieldValue(bw.getWrappedInstance(), name, property);
        }
    }

    /**
     * 执行AwareMethod --—> BeanPostProcessor的前置方法 ---> bean的生命周期init方法 ---> BeanPostProcessor的后置方法
     *
     * @param beanName
     * @param bean
     * @param mbd
     * @return
     */
    protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
        //1. factory中的 Aware接口注入
        invokeAwareMethods(beanName, bean);

        Object wrappedBean = bean;
        //2.BeanPostProcessor的前置方法
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        try {
            //3. bean的生命周期init方法 
            invokeInitMethods(beanName, wrappedBean, mbd);
        } catch (Throwable ex) {
            throw new RuntimeException("invokeInitMethods error", ex);
        }
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        return wrappedBean;
    }

    @Override
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessBeforeInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }


    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

    @Override
    protected Object postProcessObjectFromFactoryBean(Object object, String beanName) {
        return applyBeanPostProcessorsAfterInitialization(object, beanName);
    }

    protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) throws Throwable {
        //TODO bean的 init生命周期方法被调用
        //实现 InitializingBean接口
        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean) {
            ((InitializingBean) bean).afterPropertiesSet();
        }
        //配置init-method 、注解@PostConstruct  (简化处理)
        if (mbd != null) {
            String[] initMethodNames = mbd.getInitMethodNames();
            if (initMethodNames != null) {
                for (String initMethodName : initMethodNames) {
                    //避免重复调用afterPropertiesSet
                    if (!(isInitializingBean && "afterPropertiesSet".equals(initMethodName))) {
                        Method method = mbd.getBeanClass().getMethod(initMethodName);
                        ReflectionUtils.makeAccessible(method);
                        method.invoke(bean);
                    }
                }
            }
        }
    }

    private void invokeAwareMethods(String beanName, Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            if (bean instanceof BeanClassLoaderAware) {
                ClassLoader bcl = getBeanClassLoader();
                if (bcl != null) {
                    ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
                }
            }
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
            }
        }
    }


}
